#include "infrastructure/map/map_extend_loader.h"

#include <QCoreApplication>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QJsonArray>
#include <QJsonDocument>
#include <QStringList>

namespace infrastructure {

MapExtendLoader::MapExtendLoader(std::shared_ptr<kernel::ILogger> logger)
    : m_logger(std::move(logger)) {}

kernel::Result<kernel::MapExtendedData> MapExtendLoader::loadExtendedData(const QString& itemName) const {
    if (itemName.isEmpty()) {
        return kernel::Result<kernel::MapExtendedData>::failure(
            kernel::ErrorCode::InvalidParameter, "Map item name is empty");
    }

    const QString baseDir = QCoreApplication::applicationDirPath();
    QString targetPath;
    const QString normalizedInput = QDir::cleanPath(itemName);
    const QFileInfo identifierInfo(normalizedInput);

    auto tryResolve = [&](const QStringList& candidates) -> QString {
        for (const auto& candidate : candidates) {
            QFileInfo info(candidate);
            if (!info.isAbsolute()) {
                info = QFileInfo(baseDir + "/" + candidate);
            }
            if (info.exists() && info.isFile()) {
                return info.absoluteFilePath();
            }
        }
        return QString();
    };

    QStringList candidates;
    candidates.append(normalizedInput);
    if (!identifierInfo.isAbsolute()) {
        const QString fileName = identifierInfo.fileName();
        candidates.append(baseDir + "/" + normalizedInput);
        candidates.append("config/" + normalizedInput);
        candidates.append("maps/" + normalizedInput);
        if (!fileName.isEmpty() && fileName != normalizedInput) {
            candidates.append("config/" + fileName);
            candidates.append("maps/" + fileName);
        }
    }

    targetPath = tryResolve(candidates);

    if (targetPath.isEmpty()) {
        QString fallbackName = identifierInfo.completeBaseName();
        if (fallbackName.isEmpty()) {
            fallbackName = normalizedInput;
        }

        QStringList fallbackCandidates;
        fallbackCandidates.append("config/" + fallbackName + ".esmap");
        fallbackCandidates.append("maps/" + fallbackName + ".esmap");

        targetPath = tryResolve(fallbackCandidates);
    }

    if (targetPath.isEmpty()) {
        QString message = QString("Map extend file not found for '%1'").arg(itemName);
        if (m_logger) {
            m_logger->warning(message);
        }
        return kernel::Result<kernel::MapExtendedData>::failure(kernel::ErrorCode::FileNotFound, message);
    }

    QFile file(targetPath);
    if (!file.open(QIODevice::ReadOnly)) {
        QString message = QString("Cannot open map extend file: %1").arg(targetPath);
        if (m_logger) {
            m_logger->error(message);
        }
        return kernel::Result<kernel::MapExtendedData>::failure(kernel::ErrorCode::FileAccessDenied, message);
    }

    const QByteArray data = file.readAll();
    file.close();

    QJsonParseError parseError;
    const QJsonDocument doc = QJsonDocument::fromJson(data, &parseError);
    if (parseError.error != QJsonParseError::NoError) {
        QString message = QString("Failed to parse extend file: %1").arg(parseError.errorString());
        if (m_logger) {
            m_logger->error(message);
        }
        return kernel::Result<kernel::MapExtendedData>::failure(kernel::ErrorCode::ParseError, message);
    }

    return parseJson(doc.object());
}

kernel::Result<kernel::MapExtendedData> MapExtendLoader::parseJson(const QJsonObject& rootObj) const {
    kernel::MapExtendedData data;

    if (rootObj.contains("AdvPointExtend")) {
        const QJsonObject advObj = rootObj.value("AdvPointExtend").toObject();
        for (auto it = advObj.begin(); it != advObj.end(); ++it) {
            const QString mapName = it.key();
            const QJsonArray stations = it.value().toArray();
            for (const auto& stationValue : stations) {
                const QJsonObject stationObj = stationValue.toObject();
                kernel::MapStationExtension extension;
                extension.mapName = mapName;
                extension.stationName = stationObj.value("instance_name").toString();
                extension.stationType = stationTypeFromInt(stationObj.value("type").toInt());
                data.stationExtensions.append(extension);
            }
        }
    }

    if (rootObj.contains("Lift")) {
        const QJsonArray liftArray = rootObj.value("Lift").toArray();
        for (const auto& liftValue : liftArray) {
            const QJsonObject liftObj = liftValue.toObject();
            kernel::LiftConfig liftConfig;
            liftConfig.liftNum = liftObj.value("lift_num").toInt();
            liftConfig.liftType = liftObj.value("lift_type").toString();
            liftConfig.agvControl = liftObj.value("agv_control").toBool();
            liftConfig.liftIp = liftObj.value("lift_ip").toString();
            liftConfig.liftPort = liftObj.value("lift_port").toInt();
            liftConfig.baseUrl = liftObj.value("base_url").toString();
            liftConfig.liftCode = liftObj.value("lift_code").toString();
            liftConfig.username = liftObj.value("username").toString();
            liftConfig.password = liftObj.value("password").toString();

            const QJsonArray floorInfoArray = liftObj.value("floor_info").toArray();
            for (const auto& floorValue : floorInfoArray) {
                const QJsonObject floorObj = floorValue.toObject();
                kernel::LiftFloorConfig floorConfig;
                floorConfig.floor = floorObj.value("floor_num").toInt();
                floorConfig.mapName = floorObj.value("map_name").toString();
                if (floorConfig.mapName.isEmpty() && floorObj.contains("mapName")) {
                    floorConfig.mapName = floorObj.value("mapName").toString();
                }
                floorConfig.askPoint = floorObj.value("ask_point").toString();
                floorConfig.waitPoint = floorObj.value("wait_point").toString();
                floorConfig.interPoint = floorObj.value("inter_point").toString();
                liftConfig.floorInfo.append(floorConfig);
            }

            data.lifts.append(liftConfig);
        }
    }

    if (rootObj.contains("Caller")) {
        const QJsonArray callerArray = rootObj.value("Caller").toArray();
        for (const auto& callerValue : callerArray) {
            const QJsonObject callerObj = callerValue.toObject();
            kernel::CallerConfig callerConfig;
            callerConfig.callerNum = callerObj.value("caller_num").toInt();
            callerConfig.taskName = callerObj.value("task_name").toString();
            data.callers.append(callerConfig);
        }
    }

    if (rootObj.contains("ThirdArea")) {
        const QJsonArray thirdArray = rootObj.value("ThirdArea").toArray();
        for (const auto& thirdValue : thirdArray) {
            const QJsonObject thirdObj = thirdValue.toObject();
            kernel::ThirdAreaConfig areaConfig;
            QString configuredId = thirdObj.value("area_id").toString();
            if (configuredId.isEmpty()) {
                configuredId = thirdObj.value("id").toString();
            }
            areaConfig.areaId = configuredId;
            areaConfig.floor = thirdObj.value("floor").toInt(0);

            const QJsonValue liftValue = thirdObj.value("lift_num");
            int liftNum = 0;
            if (liftValue.isString()) {
                liftNum = liftValue.toString().toInt();
            } else if (!liftValue.isUndefined() && !liftValue.isNull()) {
                liftNum = liftValue.toInt();
            }
            if (liftNum > 0) {
                areaConfig.liftNum = liftNum;
                areaConfig.isLiftResource = true;
            }

            if (!areaConfig.areaId.isEmpty()) {
                data.thirdAreas.append(areaConfig);
            }
        }
    }

    if (rootObj.contains("ChargeStations")) {
        const QJsonArray chargeArray = rootObj.value("ChargeStations").toArray();
        for (const auto& chargeValue : chargeArray) {
            const QJsonObject chargeObj = chargeValue.toObject();
            kernel::ChargeStationConfig chargeConfig;
            chargeConfig.pointId = chargeObj.value("point_id").toString();
            chargeConfig.stationPoint = chargeObj.value("station_point").toString();
            chargeConfig.actionPoint = chargeObj.value("action_point").toString();
            chargeConfig.areaId = chargeObj.value("area_id").toString();
            chargeConfig.floor = chargeObj.value("floor").toInt();

            const QJsonArray assignedArray = chargeObj.value("assigned_agv_numbers").toArray();
            for (const auto& agvValue : assignedArray) {
                if (agvValue.isDouble()) {
                    chargeConfig.assignedAgvNumbers.append(static_cast<int>(agvValue.toDouble()));
                }
            }

            const QJsonArray blockedArray = chargeObj.value("blocked_agv_numbers").toArray();
            for (const auto& agvValue : blockedArray) {
                if (agvValue.isDouble()) {
                    chargeConfig.blockedAgvNumbers.append(static_cast<int>(agvValue.toDouble()));
                }
            }

            const QJsonArray supportedTypes = chargeObj.value("supported_types").toArray();
            for (const auto& typeValue : supportedTypes) {
                if (typeValue.isString()) {
                    chargeConfig.supportedAgvTypes.append(typeValue.toString());
                }
            }

            data.chargeStations.append(chargeConfig);
        }
    }

    if (rootObj.contains("ParkStations")) {
        const QJsonArray parkArray = rootObj.value("ParkStations").toArray();
        for (const auto& parkValue : parkArray) {
            const QJsonObject parkObj = parkValue.toObject();
            kernel::ParkStationConfig parkConfig;
            parkConfig.pointId = parkObj.value("point_id").toString();
            parkConfig.stationPoint = parkObj.value("station_point").toString();
            parkConfig.areaId = parkObj.value("area_id").toString();
            parkConfig.floor = parkObj.value("floor").toInt();
            data.parkStations.append(parkConfig);
        }
    }

    if (rootObj.contains("AutoDoor")) {
        const QJsonArray autoDoorArray = rootObj.value("AutoDoor").toArray();
        for (const auto& doorValue : autoDoorArray) {
            const QJsonObject doorObj = doorValue.toObject();
            kernel::AutoDoorConfig doorConfig;
            doorConfig.doorId = doorObj.value("door_id").toInt();
            doorConfig.deviceId = static_cast<quint32>(doorObj.value("device_id").toVariant().toUInt());
            doorConfig.floor = doorObj.value("floor").toInt(0);  // 默认0楼
            doorConfig.point1 = doorObj.value("point1").toString();
            doorConfig.point2 = doorObj.value("point2").toString();

            // 解析LED掩码（可选字段，有默认值）
            if (doorObj.contains("open_mask")) {
                doorConfig.openMask = static_cast<quint8>(doorObj.value("open_mask").toInt(0x07));
            }
            if (doorObj.contains("close_mask")) {
                doorConfig.closeMask = static_cast<quint8>(doorObj.value("close_mask").toInt(0x00));
            }

            data.autoDoors.append(doorConfig);
        }
    }

    return kernel::Result<kernel::MapExtendedData>::success(data);
}

kernel::StationType MapExtendLoader::stationTypeFromInt(int value) const {
    switch (value) {
        case 1:
            return kernel::StationType::Charge;
        case 2:
            return kernel::StationType::Fork;
        case 3:
            return kernel::StationType::Park;
        default:
            return kernel::StationType::Normal;
    }
}

}  // namespace infrastructure
